//---------------------------------------------------------------------
// File: XmlValidationStepEx.cs
// 
// Summary: 
//
//---------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, WHETHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//---------------------------------------------------------------------

namespace Microsoft.Services.BizTalkApplicationFramework.BizUnit
{
    using System;
    using System.IO;
    using System.Xml;
    using System.Xml.Schema;
    using System.Xml.XPath;

    /// <summary>
    /// The XmlValidationStepEx validates an Xml document, it may validate against a given schema, and also evaluate XPath queries.
    /// The Xpath query is extended from XmlValidationStep to allow Xpath functions to be used which may not return a node set.
    /// </summary>
    /// 
    /// <remarks>
    /// The following shows an example of the Xml representation of this test step.
    /// 
    /// <code escaped="true">
    ///	<ValidationStep assemblyPath="" typeName="Microsoft.Services.UK.TestFramework.XmlValidationStepEx">
    ///		<XmlSchemaPath>.\TestData\PurchaseOrder.xsd</XmlSchemaPath>
    ///		<XmlSchemaNameSpace>urn:bookstore-schema</XmlSchemaNameSpace>
    ///		<XPathList>
    ///			<XPathValidation query="/*[local-name()='PurchaseOrder' and namespace-uri()='http://SendMail.PurchaseOrder']/*[local-name()='PONumber' and namespace-uri()='']">PONumber_0</XPathValidation>
    ///		</XPathList>
    ///	</ValidationStep>
    ///	</code>
    ///	
    ///	<list type="table">
    ///		<listheader>
    ///			<term>Tag</term>
    ///			<description>Description</description>
    ///		</listheader>
    ///		<item>
    ///			<term>XmlSchemaPath</term>
    ///			<description>The XSD schema to use to validate the XML data (optional)</description>
    ///		</item>
    ///		<item>
    ///			<term>XmlSchemaNameSpace</term>
    ///			<description>The XSD schema namespace to validate the XML data against (optional)</description>
    ///		</item>
    ///		<item>
    ///			<term>XPathList/XPathValidation</term>
    ///			<description>XPath expression to evaluate against the XML document (optional)(repeating).</description>
    ///		</item>
    ///	</list>
    ///	</remarks>	

    public class XmlValidationStepEx : IValidationStep
    {
        /// <summary>
        /// ITestStep.Execute() implementation
        /// </summary>
        /// <param name='data'>The stream cintaining the data to be validated.</param>
        /// <param name='validatorConfig'>The Xml fragment containing the configuration for the test step</param>
        /// <param name='context'>The context for the test, this holds state that is passed beteen tests</param>
        public void ExecuteValidation(Stream data, XmlNode validatorConfig, Context context)
        {
            string xmlSchemaPath = context.ReadConfigAsString( validatorConfig, "XmlSchemaPath", true );
            string ns = context.ReadConfigAsString( validatorConfig, "XmlSchemaNameSpace", true );
            XmlNodeList xpaths = validatorConfig.SelectNodes( "XPathList/*" );

            XmlDocument doc = new XmlDocument();
            XmlTextReader trData = new XmlTextReader( data );
            XmlValidatingReader vr = new XmlValidatingReader(trData);

            // If schema was specified use it to vaidate against...
            if ( null != xmlSchemaPath )
            {
                MemoryStream xsdSchema = StreamHelper.LoadFileToStream(xmlSchemaPath);
                XmlTextReader trSchema = new XmlTextReader( xsdSchema );
                XmlSchemaCollection xsc = new XmlSchemaCollection();

                if ( null != ns )
                {
                    xsc.Add( ns, trSchema );
                    vr.Schemas.Add(xsc);
                }

                doc.Load(vr);
            }

            data.Seek(0, SeekOrigin.Begin);
            doc.Load(data);

            foreach( XmlNode xpath in xpaths)
            {
                string xpathExp = xpath.SelectSingleNode("@query").Value;
                XmlNode valNode = xpath.SelectSingleNode(".");

                string nodeValue = valNode.InnerText;

                context.LogInfo("XmlValidationStepEx evaluting XPath {0} equals \"{1}\"", xpathExp, nodeValue);

                XPathNavigator xpn = doc.CreateNavigator();
                object result = xpn.Evaluate(xpathExp);
                
                string checkValue = null;
                if (result.GetType().Name == "XPathSelectionIterator")
                {
                    XPathNodeIterator xpi = result as XPathNodeIterator;
                    xpi.MoveNext();
                    if (null != xpi)
                    {
                        checkValue = xpi.Current.ToString();
                    }
                }
                else
                {
                    checkValue = result.ToString();
                }

                if ( 0 != nodeValue.CompareTo( checkValue ) )
                {
                    throw new ApplicationException(string.Format("XmlValidationStepEx failed, compare {0} != {1}, xpath query used: {2}", nodeValue, checkValue, xpathExp));
                }
            }
        }
    }
}
